﻿package org.apache.xinclude4flex.loaders {
	
	/**
	 * @author gavingui2011@gmail.com (Gavin Lei)
	 */
	
	import flash.errors.IllegalOperationError;
	
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.events.EventDispatcher;
	import flash.events.HTTPStatusEvent;
	import flash.events.ErrorEvent;
	import flash.events.ProgressEvent;
		
	import flash.net.URLRequest;
	import flash.net.URLLoader;
	import flash.net.URLRequestMethod;
	
	[Event(name = "error", type = "flash.events.ErrorEvent")];
	[Event(name = "complete", type = "flash.events.Event")];
	public class XMLLoader extends EventDispatcher{
		
		protected var _loader		:URLLoader;
		protected var _request		:URLRequest;
		
		protected var _loading		:Boolean;
		protected var _loadComplete	:Boolean;
		protected var _loadSuccess	:Boolean;
		protected var _xml			:XML;
		
		public function XMLLoader():void {
			XML.ignoreWhitespace = true;
			setupLoader();
		}
		
		/**
		 *
		 * @param	url
		 * @param	forced - if already loading, this will force the current connection to close and load the new url
		 */
		public function load(url:String, forced:Boolean = false):void {
			
			if (_loading && forced) { abortLoad(); }
			
			if (!_loading) {
				_request.url = url;
				_loading 	 = true;
				_loader.load(_request);
			}else{
				throw new IllegalOperationError("Already loading data, use the 'forced' parameter to close the current connection and load the new one");
			}
		}
		
		/**
		 * abort the current connection
		 */
		public function abortLoad():void {
			if(_loading){
				_loader.close();
				_loading = false;
			}
		}
		
		override public function dispatchEvent(event:Event):Boolean {
			if (hasEventListener(event.type)) { return super.dispatchEvent(event); }
			return true;
		}
		
		/**
		 * The loaded xml data
		 */
		public function get xml():XML { return _xml; }
		
		/**
		 * If currently loading
		 */
		public function get loading():Boolean { return _loading; }
		
		/**
		 * If the current load has completed - does not indicate success or error
		 */
		public function get loadComplete():Boolean { return _loadComplete; }
		
		/**
		 * If the last load was successful
		 */
		public function get loadSuccess():Boolean { return _loadSuccess; }
			
		protected function setupLoader():void {
			_loader 		= new URLLoader();
			setupLoaderEvents();
			
			_request 		= new URLRequest();
			_request.method = URLRequestMethod.GET;
			
			_loading 		= false;
			_loadComplete 	= false;
			_loadSuccess	= false;
		}
		
		protected function setupLoaderEvents():void {
			_loader.addEventListener(Event.COMPLETE, onLoadComplete);
			_loader.addEventListener(Event.CLOSE, onLoadClose);
			_loader.addEventListener(Event.OPEN, onLoadOpen);
			_loader.addEventListener(ProgressEvent.PROGRESS, onLoadProgress);
			_loader.addEventListener(IOErrorEvent.IO_ERROR, onLoadError);
			_loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onLoadError);
		}
		
		protected function cleanupLoaderEvents():void {
			_loader.removeEventListener(Event.COMPLETE, onLoadComplete);
			_loader.removeEventListener(Event.CLOSE, onLoadClose);
			_loader.removeEventListener(Event.OPEN, onLoadOpen);
			_loader.removeEventListener(ProgressEvent.PROGRESS, onLoadProgress);
			_loader.removeEventListener(IOErrorEvent.IO_ERROR, onLoadError);
			_loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onLoadError);
		}
		
		
		private function onLoadOpen(e:Event):void {
			dispatchEvent(new Event(Event.OPEN));
		}
		
		private function onLoadClose(e:Event):void {
			dispatchEvent(new Event(Event.CLOSE));
		}
		
		protected function onLoadProgress(e:ProgressEvent):void {
			dispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS, false, false, e.bytesLoaded, e.bytesTotal));
		}
		
		protected function onLoadComplete(e:Event):void {
			
			_loading 		= false;
			_loadComplete 	= true;
			
			if (_loader.data is String){
				try{
					_xml = new XML(_loader.data as String);
				}catch (err:TypeError) {
					if (!hasEventListener(ErrorEvent.ERROR)) { throw err; }
					dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, "Unable to parse data - not valid xml"));
				}
			}else{
				if (!hasEventListener(ErrorEvent.ERROR)) { throw new Error("Uncaught exception - Loaded data is not String, can not create XML"); }
				dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, "Loaded data is not String, can not create XML"));
				return;
			}
			_loadSuccess = true;
			dispatchEvent(new Event(Event.COMPLETE));
		}
		
		protected function onLoadError(e:ErrorEvent):void {
			_loading 		= false;
			_loadSuccess 	= false;
			_loadComplete 	= true;
			trace(e.toString());
			if (!hasEventListener(ErrorEvent.ERROR)) { throw new Error("Uncaught exception - " + e.text); }
			dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, e.text));
		}
	}
}